package parser

import (
	"bufio"
	"errors"
	"go-redis/interface/resp"
	"go-redis/lib/logger"
	"go-redis/resp/reply"
	"io"
	"runtime/debug"
	"strconv"
	"strings"
)

/**
  在RESP中，根据响应信息的第一个字节来判断数据的类型:
   + 表示是一个Simple Strings
      当Redis服务端使用Simple Strings进行响应的时候，客户端需要返回给调用者一个字符串，这个字符串是由加号（+）以后的字符串组成  +OK\r\n
   - 表示是错误信息Errors
      减号（-）后面的第一个字符到第一个空格或者换行符之间的字符串，表示的是错误的种类 -ERR unknown command ‘onmpw’
   ：表示是整型数据
      integer 类型的第一个字节是":"(冒号), 后面接着的是整数,最后以CRLF(\r\n)结尾，例如  ":1000\r\n"
   $ 表示是Bulk Strings
      用$符号后面跟着一个整数的形式组成一个字符串（长度前缀），以CRLF结尾, $5\r\nonmpw\r\n
   * 表示是数组
     星号（*）作为第一个字节，后面跟着一个十进制的整数，接着是CRLF（\r\n）,"*5\r\n:1\r\n:2\r\n:3\r\n:4\r\n$5\r\nonmpw\r\n"
     服务端发送的第一行是*5\r\n，表示后面跟着有5个响应信息。每一条响应信息组成了一个Multi Bulk响应被传输到客户端
*/

// Payload store redis的返回值以及错误信息
type Payload struct {
	Data resp.Reply
	Err  error
}

// readState 记录读的状态信息
type readState struct {
	readingMultiLine  bool
	expectedArgsCount int
	msgType           byte
	args              [][]byte
	bulkLen           int64
}

// 判断是否已经读取结束
func (r *readState) finished() bool {
	return r.expectedArgsCount > 0 && len(r.args) == r.expectedArgsCount
}

// ParserStream 解析tcp的字节流
func ParserStream(reader io.Reader) <-chan *Payload {

	ch := make(chan *Payload)

	go parse0(reader, ch)

	return ch
}

// 解析字节流的逻辑
func parse0(reader io.Reader, ch chan<- *Payload) {

	defer func() {
		if err := recover(); err != nil {
			logger.Error(string(debug.Stack()))
		}
	}()

	bufReader := bufio.NewReader(reader)

	var state readState
	var err error
	var msg []byte

	for {
		var ioErr bool
		msg, ioErr, err = readLine(bufReader, &state)
		if err != nil {
			if ioErr {
				ch <- &Payload{
					Err: err,
				}
				close(ch)
				return
			}
			ch <- &Payload{
				Err: err,
			}
			continue
		}
		// parser single line
		if !state.readingMultiLine {
			if msg == nil {
				continue
			}
			// 解析数组格式的redis协议
			if msg[0] == '*' {
				// 解析数组格式的协议头，主要读取数据的长度
				err := parseMultiBulkHeader(msg, &state)
				if err != nil {
					ch <- &Payload{
						Err: errors.New("protocol error: " + string(msg)),
					}
					// 读取状态值重置
					state = readState{}
					continue
				}
				// 若要读取的数据的长度为0，返回空的数组
				if state.expectedArgsCount == 0 {
					ch <- &Payload{
						Data: &reply.EmptyMultiBulkReply{},
					}
					// 读取状态值重置
					state = readState{}
					continue
				}
			} else if msg[0] == '$' {
				// 解析bulk的字符串格式
				err := parseBulkHeader(msg, &state)
				if err != nil {
					ch <- &Payload{
						Err: errors.New("protocol error: " + string(msg)),
					}
					state = readState{} // reset state
					continue
				}
				if state.bulkLen == -1 {
					ch <- &Payload{
						Data: &reply.NullBulkReply{},
					}
					// 读取状态值重置
					state = readState{}
					continue
				}
			} else {
				result, err := parseSingleLineReply(msg)
				ch <- &Payload{
					Data: result,
					Err:  err,
				}
				// 读取状态值重置
				state = readState{}
				continue
			}
		} else {
			// 读取body信息
			err := readBody(msg, &state)
			if err != nil {
				ch <- &Payload{
					Err: errors.New("protocol error: " + string(msg)),
				}
				// 读取状态值重置
				state = readState{}
				continue
			}
			// 如果body信息已经读完毕了
			if state.finished() {
				var result resp.Reply
				if state.msgType == '*' {
					result = reply.MakeMultiBulkReply(state.args)
				} else if state.msgType == '$' {
					result = reply.MakeBulkReply(state.args[0])
				}
				ch <- &Payload{
					Data: result,
					Err:  err,
				}
				// 读取状态值重置
				state = readState{}
			}
		}
	}
}

// parseMultiBulkHeader:解析数组格式的协议
func parseMultiBulkHeader(msg []byte, state *readState) error {
	var err error
	var expectedLine uint64
	// 读取数组类型的长度
	expectedLine, err = strconv.ParseUint(string(msg[1:len(msg)-2]), 10, 64)
	if err != nil {
		return errors.New("protocol error: " + string(msg))
	}
	if expectedLine == 0 {
		state.expectedArgsCount = 0
		return nil
	} else if expectedLine > 0 {
		state.msgType = msg[0]
		state.readingMultiLine = true
		state.expectedArgsCount = int(expectedLine)
		state.args = make([][]byte, 0, expectedLine)
		return nil
	} else {
		return errors.New("protocol error: " + string(msg))
	}
}

// parseBulkHeader 解析bulk string格式的协议头
func parseBulkHeader(msg []byte, state *readState) error {
	var err error
	// 读取bulk string的长度
	state.bulkLen, err = strconv.ParseInt(string(msg[1:len(msg)-2]), 10, 64)
	if err != nil {
		return errors.New("protocol error: " + string(msg))
	}
	if state.bulkLen == -1 { // null bulk
		return nil
	} else if state.bulkLen > 0 {
		state.msgType = msg[0]
		state.readingMultiLine = true
		state.expectedArgsCount = 1
		state.args = make([][]byte, 0, 1)
		return nil
	} else {
		return errors.New("protocol error: " + string(msg))
	}
}

// parseSingleLineReply:解析单行格式的协议，类似于 "+OK\r\n" "-Error message\r\n"
func parseSingleLineReply(msg []byte) (resp.Reply, error) {
	// 截取字符串
	str := strings.TrimSuffix(string(msg), "\r\n")
	var result resp.Reply
	switch msg[0] {
	case '+':
		result = reply.MakeStatusReply(str[1:])
	case '-':
		result = reply.MakeErrReply(str[1:])
	case ':':
		val, err := strconv.ParseInt(str[1:], 10, 64)
		if err != nil {
			return nil, errors.New("protocol error: " + string(msg))
		}
		result = reply.MakeIntReply(val)
	}
	return result, nil
}

// readLine：读取一行数据，根据\n截取数据
func readLine(reader *bufio.Reader, state *readState) ([]byte, bool, error) {
	var msg []byte
	var err error
	if state.bulkLen == 0 {
		msg, err = reader.ReadBytes('\n')
		if err != nil {
			return nil, true, err
		}
		if len(msg) == 0 || msg[len(msg)-1] != '\n' {
			return nil, false, errors.New("protocol error: " + string(msg))
		}
	} else {
		// 读取字节的数据
		msg = make([]byte, state.bulkLen+2)
		_, err = io.ReadFull(reader, msg)
		if err != nil {
			return nil, true, err
		}
		if len(msg) == 0 || msg[len(msg)-2] != '\r' || msg[len(msg)-1] != '\n' {
			return nil, false, errors.New("protocol error: " + string(msg))
		}
		state.bulkLen = 0
	}
	return msg, false, nil
}

// readBody：读取消息体
func readBody(msg []byte, state *readState) error {
	line := msg[0 : len(msg)-2]
	var err error
	if line[0] == '$' {
		state.bulkLen, err = strconv.ParseInt(string(line[1:len(msg)-2]), 10, 64)
		if err != nil {
			return errors.New("protocol error: " + string(msg))
		}
		if state.bulkLen < 0 {
			state.args = append(state.args, []byte{})
			state.bulkLen = 0
		}
	} else {
		state.args = append(state.args, line)
	}
	return nil
}
